#include "vcl_stl.h"

#include "bkbase.h"
#include "h_base.h"
#include "h_xml.h"
using namespace h_base;

#include "simbat.h"
using namespace simbat;

#include <sys/types.h>
#include <sys/stat.h>
#include <glob.h>

struct HCleanInfo
{
   HString Path;
   int Days;

   HCleanInfo()
      : Days(7)
   {}
};

static char *SimbatBase = NULL;
static pid_t ParentPid;
static vector<HCleanInfo> CleanVec;
static HString SyslogDir;
static HSyslog Syslog;

static void LoadFromCfg(HXmlTree &Cfg)
{
   SyslogDir = EnvEvaluate(Cfg.GetValue("SYSLOG_DIR", "$SIMBAT_BASE/etc"));
   Syslog.Setup(SyslogDir, Cfg.GetValueUpper("SYSLOG_LEVEL", "INFO"));

   vector<HXmlTree*> Nodes;

   Cfg.GetChildren("CLEAN", Nodes);

   for ( int i = 0; i < Nodes.size(); i++ )
   {
      HXmlTree *Node = Nodes[i];
      HCleanInfo Clean;
      Clean.Days = StrToInt(Node->GetValue("DAYS", "7").c_str());
      vector<HXmlTree *> PathNodes;
      Node->GetChildren("PATH", PathNodes);
      for ( int j = 0; j < PathNodes.size(); j++ )
      {
         Clean.Path = PathNodes[j]->GetData();
         CleanVec.push_back(Clean);
      }
   }
}

static bool LoadCfg(HString &ErrStr)
{
   char FileName[500 + 1];
   HXmlTree Cfg;

   {
      HFileLockRead Lock("$SIMBAT_BASE/tmp/config.lck");
      snprintf(FileName, 500, "%s/etc/config.xml", SimbatBase);
      if ( !Cfg.LoadFromFile(FileName) )
      {
         ErrStr = Cfg.GetErrorString();
         return false;
      }
   }

   LoadFromCfg(Cfg);
   return true;
}

static void RemoveFiles(time_t CurrTime, const glob_t &Glob, int Days)
{
   char ErrMsg[2048 + 1];
   time_t LastModTime = CurrTime - Days * 60 * 60 * 24;

   for ( int i = 0; i < Glob.gl_pathc; i++ )
   {
      if ( i % 16 == 0 && kill(ParentPid, 0) == -1 )
      {
         break;
      }
      const char *FileName = Glob.gl_pathv[i];
      struct stat Stat;
      if ( stat(FileName, &Stat) == -1 )
      {
         snprintf(ErrMsg, 2048, "stat(%s) fail, errno = %d", FileName, errno);
         Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
         continue;
      }
      if ( strchr(FileName, '\'') != NULL || strchr(FileName, '\\') != NULL )
      {
         snprintf(ErrMsg, 2048, "Invalid file name (%s)", FileName);
         Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
         continue;
      }
      if ( Stat.st_mtime < LastModTime )
      {
         if ( S_ISDIR(Stat.st_mode) )
         {
            char Command[2048 + 1];
            snprintf(Command, 2048, "rm -rf '%s'", FileName);
            system(Command);
         }
         else
         {
            int r = unlink(FileName);
            if ( r != 0 )
            {
               snprintf(ErrMsg, 2048, "unlink(%s) fail, errno = %d", FileName, errno);
               Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
            }
         }
      }
   }
}

int main()
{
   SimbatBase = getenv("SIMBAT_BASE");

   if ( SimbatBase == NULL )
   {
      BkPrintError("Line %d : $SIMBAT_BASE not defined", __LINE__);
      return 8;
   }

   HString ErrStr;

   if ( !LoadCfg(ErrStr) )
   {
      BkPrintError("Load config.xml fail, %s", ErrStr.c_str());
      return 8;
   }

   ParentPid = getppid();

   time_t LastActionTime = 0;

   for ( ; ; sleep(2) )
   {
      if ( kill(ParentPid, 0) == -1 )
      {
         break;
      }
      time_t CurrTime = time(NULL);
      struct tm Tm;
      BkLocalTime(&CurrTime, &Tm);
      if ( CurrTime - LastActionTime < 60 * 60 * 24 || Tm.tm_hour != 4 )
      {
         continue;
      }
      LastActionTime = CurrTime;
      for ( int i = 0; i < CleanVec.size(); i++ )
      {
         if ( kill(ParentPid, 0) == -1 )
         {
            break;
         }
         HCleanInfo &Clean = CleanVec[i];
         HString Path = EnvEvaluate(Clean.Path);
         struct stat Stat;
         if ( stat(Path.c_str(), &Stat) == 0 && S_ISDIR(Stat.st_mode) )
         {
            Path = Path + "/*";
         }
         glob_t Glob;
         memset(&Glob, sizeof(Glob), 0);
         if ( glob(Path.c_str(), 0, NULL, &Glob) == 0 )
         {
            RemoveFiles(CurrTime, Glob, Clean.Days);
            globfree(&Glob);
         }
      }
   }

   return 0;
}
